import { Transaction } from 'tiny-entity2';
import { PageType } from './../../domain/models/page';
import { TemplateService } from './../../service/templateService';
import { Guid } from './../../common/utils/guid';
import { ApiDataContext } from './../../domain/apiDataContext';
import { FlowNodeQueryParams, FlowNodeService } from './../../service/flowNodeService';
import { PageService } from './../../service/pageService';
import { FlowNode } from './../../domain/models/flowNode';
import * as Koa from 'koa';
import { route, RouteOptionsVerify, ServiceManager, UsingLock } from "../../common";
import { ServiceException } from '../../common/excpetion';
import { Page } from '../../domain/models/page';
import { ConditionService } from '../../service/conditionService';
import Enumerable = require('linq');
import md5 = require('md5');
import { DataSourceService } from '../../service/dataSourceService';
const pageSvr = ServiceManager.Current.GetService<PageService>('PageService');
const nodeSvr = ServiceManager.Current.GetService<FlowNodeService>('FlowNodeService');
const tmpSvr = ServiceManager.Current.GetService<TemplateService>('TemplateService');
const conditionSvr = ServiceManager.Current.GetService<ConditionService>('ConditionService');
const dsSvr = ServiceManager.Current.GetService<DataSourceService>('DataSourceService');


class FlowNodeController {
    @route(new RouteOptionsVerify('GET', '/api/flowNode'))
    async queryFlowNodes(ctx: Koa.Context, next) {
        let q: FlowNodeQueryParams = ctx.query;
        let flowNodes = await nodeSvr.QueryFlowNode(q);

        if (q && q.needInfo) {
            let pList = [];
            for (let flowNode of flowNodes) {
                pList.push(getFlowNodeInfo(flowNode.id));
            }

            let r = await Promise.all<FlowNode>(pList);
            ctx.response.body = r;
        }
        else {
            ctx.response.body = flowNodes;
        }


        await next();
    }
    @route(new RouteOptionsVerify('GET', '/api/flowNode/:id'))
    async getFlowNodeInfoById(ctx: Koa.Context, next) {
        let { id } = ctx.params;
        let node = await getFlowNodeInfo(id);
        ctx.response.body = node;
        await next();
    }
    @route(new RouteOptionsVerify('POST', '/api/flowNode'))
    async saveFlowNode(ctx: Koa.Context, next) {
        let orgId = ctx.headers['orgid'];
        let data: FlowNode = ctx.request.body;

        if (!data.id) {
            throw new ServiceException('id不能为空！');
        }
        // if (!data.pages || !data.pages.length) {
        //     throw new ServiceException('pages不能为空！');
        // }

        await UsingLock(data.id, async () => {
            return await Transaction(new ApiDataContext(), async ctx => {
                data.orgId = orgId;
                let flowNode = await nodeSvr.GetFlowNodeById(data.id);
                if (flowNode) {
                    await pageSvr.DeletePageByNodeId(flowNode.id);
                    await nodeSvr.UpdateFlowNode(data, ctx);
                }
                else {
                    flowNode = await nodeSvr.CreateFlowNode(data, ctx);
                }

                if (data.pages && data.pages.length) {
                    let page = data.pages[0];
                    page.orgId = orgId;
                    await pageSvr.DeletePageTmpItemByPageId(page.id, ctx);
                    await tmpSvr.DeleteTemplateRefByPageId(page.id);

                    let hasPage = await pageSvr.GetPageById(page.id);
                    if (!hasPage) {
                        page.flowNodeId = flowNode.id;
                        if (!page.id) {
                            page.id = Guid.GetGuid();
                        }
                        page.type = PageType.NodePage;
                        await pageSvr.CreatePage(page, ctx);
                    }

                    for (let pitem of page.pageTmpItems) {
                        pitem.id = Guid.GetGuid();
                        pitem.pageId = page.id;
                        pitem.orgId = orgId;
                        if (!pitem.templateId) {
                            throw new ServiceException('templateId不能为空！');
                        }

                        await pageSvr.CreatePageTmpItem(pitem, ctx);

                        if (pitem.template) {
                            pitem.template.id = pitem.templateId;
                            pitem.template.orgId = orgId;
                            // let hasTmp = await tmpSvr.GetTemplateByOrgIdAndName(pitem.template.orgId, pitem.template.name);
                            let hasTmp = await tmpSvr.HasTemplate(pitem.templateId);
                            if (hasTmp) {
                                hasTmp.orgId = orgId;
                                await tmpSvr.UpdateTemplate(pitem.template, ctx);
                            }
                            else {
                                await tmpSvr.CreateTemplate(pitem.template, ctx);
                            }

                            if (pitem.template.condition && pitem.template.condition.name) {
                                await conditionSvr.DeleteCondition(pitem.template.condition.id, ctx);
                                pitem.template.condition.id = Guid.GetGuid();
                                pitem.template.condition.objectId = pitem.templateId;
                                pitem.template.condition.objectType = 'Template';
                                pitem.template.condition.orgId = orgId;

                                await conditionSvr.CreateCondition(pitem.template.condition, ctx);
                            }

                            for (let tmItem of pitem.template.tmpComItems) {
                                if (!tmItem.componentId) {
                                    throw new ServiceException('未传入componentId');
                                }
                                if (!tmItem.id) {
                                    tmItem.id = Guid.GetGuid();
                                }

                                tmItem.templateId = pitem.templateId;
                                tmItem.orgId = orgId;

                                let hasTmpItem = await tmpSvr.GetTmpComItemById(tmItem.id);
                                if (hasTmpItem) {
                                    await tmpSvr.UpdateTmpComItem(tmItem, ctx);
                                } else {
                                    await tmpSvr.CreateTmpComItem(tmItem, ctx);
                                }


                                if (tmItem.condition) {
                                    tmItem.condition.id = Guid.GetGuid();
                                    tmItem.condition.objectId = tmItem.id;
                                    tmItem.condition.objectType = 'TmpComItem';
                                    tmItem.condition.orgId = orgId;
                                    await conditionSvr.CreateCondition(tmItem.condition, ctx);
                                }
                            }
                        }
                    }

                    // await pageSvr.CreatePageTmpItems(page.pageTmpItems, ctx);
                }
            })
        });

        ctx.response.body = data;
        await next();
    }
    @route(new RouteOptionsVerify('PUT', '/api/flowNodes'))
    async updateFlowNode(ctx: Koa.Context, next) {
        const orgId = ctx.headers['orgid'];
        const flowNodes: FlowNode[] = ctx.request.body;

        if (flowNodes.length == 0) {
            throw new ServiceException('length不能为0！');
        }

        const flowNodeIds = Enumerable.from(flowNodes).orderBy(x => x.id).select(x => x.id).toArray();
        const lockKey = md5(flowNodeIds.join(''));

        await UsingLock(lockKey, async () => {
            return await Transaction(new ApiDataContext(), async ctx => {
                for (const item of flowNodes) {
                    const sourceFlowNode = await nodeSvr.GetFlowNodeById(item.id);
                    if (sourceFlowNode.childrenWorkflowId && !item.childrenWorkflowId) {
                        // 表示删除子流程
                        await dsSvr.DeleteDataSourceByObjectId(sourceFlowNode.childrenWorkflowId, ctx);
                    }
                    item.orgId = orgId;
                    await nodeSvr.UpdateFlowNode(item, ctx);
                }
            });
        });

        ctx.response.body = flowNodes;
        await next();
    }
    @route(new RouteOptionsVerify('DELETE', '/api/flowNode/:id'))
    async deleteFlowNode(ctx: Koa.Context, next) {
        let { id } = ctx.params;
        await UsingLock(id, async () => {

            return nodeSvr.DeleteFlowNode(id);
        })
        ctx.response.body = {};
        await next();
    }
}

const getFlowNodeInfo = async (id: string) => {
    let node = await nodeSvr.GetFlowNodeById(id);
    let queryResult = await pageSvr.QueryPage({ flowNodeId: id }) as Page[];

    node.pages = queryResult;
    for (let page of node.pages) {
        page.pageTmpItems = await pageSvr.GetPageTmpItemsByPageId(page.id);
        for (let item of page.pageTmpItems) {
            item.template = await tmpSvr.GetTemplateById(item.templateId);
        }
    }

    if (node.openConditionId) {
        node.openCondition = await conditionSvr.GetConditionById(node.openConditionId);
    }

    if (node.closeConditionId) {
        node.closeCondition = await conditionSvr.GetConditionById(node.closeConditionId);
    }

    return node;
}